import { Cell, Edge, Graph, Model, Node } from "@antv/x6";
import { reactive, ref } from "vue";
import { merge, set, get } from "lodash-es"
import DEFAULT_GRAPH_OPTIONS from "../options"
import { IPackAntvX6 } from "../inter"
import { DICT_EDGES,getFromStore,saveToStore } from "../util"
import Main from "../main";
import {t} from "../i18n/index"
import { useDebounceFn } from "@vueuse/core";

let th:any = undefined;
export default (main:Main) => {
    //
    const g: any = new Object();
    // 注册元素
    g.registerNode = (name: string, md: Node.Metadata, force = true) => {
        Graph.registerNode(name, md, force);
        return g;
    }

    g.registerEdge = (name: string, md: Edge.Metadata, force = true) => {
        Graph.registerEdge(name, md, force)
        return g;
    }
    // 当前画布的模式
    g.mode = ref<"dev" | "prod">('dev');
    // 是否正在加载
    g.loading = ref(false)
    //
    g.isFull = ref(false);
    g.toggleFull = () => {
        if (!document.fullscreenElement) {
            g.rootEl.requestFullscreen();
            g.isFull.value = true;
        } else if (document.exitFullscreen) {
            document.exitFullscreen();
            g.isFull.value = false;
        }
        return g;
    }
    // 全屏背景色
    g.BG = ref('transparent');
    g.setBG = (c:string) => {
        g.BG.value = c;
        return g;
    }
    g.fullBG = ref('#fff');
    g.setFullBG = (c:string) => {
        g.fullBG.value = c;
        return g;
    }
    // 动画开关
    g.animate = ref(false)
    g.startAnimate = () => {
        g.animate.value = true;
        clearTimeout(th);
        th = setTimeout(() => {
            g.animate.value = false;
        }, 500)
        return g;
    }
    //
    const options: Graph.Options = { connecting: { router: "normal", connector: "normal", allowPort: true }, interacting: { nodeMovable: true }, panning: true }

    // 常用表单属性集合
    let isUpdateConfig = false;
    const updateConfig = () => {
        isUpdateConfig = true;
        setTimeout(() => {
            isUpdateConfig = false;
        },500)
    }
    g.config = reactive({ x: 0, y: 0, scale: 100, edge: 0, move: true, panning: true, port: true, interacting: true })
    g.resetConfig = (cf: any = {}) => {
        Object.assign(g.config, cf)
        g.saveConfig(main.key);
        return g;
    }
    g.saveConfig = useDebounceFn((key = main.key) => {
        saveToStore(key,g.config)
        return g;
    },800)
    g.recoverConfig = () => {
        const c = getFromStore(main.key)
        // console.log('recoverConfig ===== ',c)
        if(c){
            Object.assign(g.config, c)
        }
        return g;
    }
    // 表单属性转换成 options 
    const configToOptions = () => {
        const { edge, move, panning, port, interacting } = g.config;
        set(options, ['panning'], panning)
        // 
        set(options, ['connecting', 'allowPort'], port)
        //
        if (interacting) {
            set(options, ['interacting', 'nodeMovable'], move)
        } else {
            set(options, ['interacting'], false)
            set(options, ['connecting', 'allowPort'], false)
        }

        if (edge >= 0 && edge < DICT_EDGES.length) {
            const e = DICT_EDGES[edge];
            set(options, ['connecting', 'router'], e.router)
            set(options, ['connecting', 'connector'], e.connector)
        }
    }
    // 重置画布属性
    g.resetOptions = (ops?: Graph.Options) => {
        if (ops) {
            Object.assign(options, ops)
        }
        configToOptions();
        return g;
    }
    // 
    const mountedHooks = new Set<(graph: Graph) => void>();
    const hooksCall = (graph: Graph) => {
        for (const f of mountedHooks) {
            if (typeof f === 'function') {
                f(graph);
            }
        }
    }
    g.onMounted = (callback: (graph: Graph) => void) => { mountedHooks.add(callback); return g; }
    // 画布组件 mounted 时
    g.mounted = (container: HTMLDivElement, graphBox: HTMLDivElement, rootBox: HTMLDivElement) => {
        g.containerEl = container;
        g.boxEl = graphBox;
        g.rootEl = rootBox;
        g.recoverConfig();
        return g;
    }
    // 初始化画布实例
    g.isInit = false
    g.initTime = ref(0)
    g.init = (ops: Graph.Options = merge({}, DEFAULT_GRAPH_OPTIONS, options)) => {
        if (!g.containerEl) {
            throw new Error(t('graph-init-nobox'))
        }
        // 生成 graph 实例 并赋值到原型链上
        const graph = new Graph({ ...ops, container: g.containerEl })
        g.__proto__ = graph;
        g.isInit = true;
        g.initTime.value = new Date().getTime();
        // 移动
        updateConfig();
        graph.zoomTo(g.config.scale / 100);
        graph.translate(g.config.x, g.config.y);
        // 初始化事件
        graph.on('scale', (e) => { 
            if(isUpdateConfig) return ;
            g.config.scale = Math.round(e.sx * 100);
            g.saveConfig(main.key);
         })
        graph.on('translate', ({ tx, ty }) => { 
            if(isUpdateConfig) return ;
            g.config.x = tx; 
            g.config.y = ty;
            //
            g.saveConfig(main.key);
        })
        // 基础布局事件

        // mountedHooks 调用
        hooksCall(graph);
        return g;
    };
    g.destory = () => {
        g?.dispose();
        // 通过克隆节点的方式清除所有的绑定事件
        const oe = g.containerEl as HTMLDivElement;
        const ce = oe.cloneNode();
        g.containerEl = ce;
        oe.parentNode?.replaceChild(ce,oe);
        return g;
    }
    // 刷新画布
    g.refresh = (data: Model.FromJSONData, options?: Model.FromJSONOptions) => {
        // 清除所有托管的 timeout interval handle
        main.local.clearHandle()
        // 清空画布
        g?.clearCells();
        // 移动
        updateConfig();
        g?.zoomTo(g.config.scale / 100);
        g?.translate(g.config.x, g.config.y);
        // 渲染数据
        g?.fromJSON(data, options)
        return g;
    }
    // 变化刷新 新增或者移除节点时获取到了全部的元素数据可以使用
    g.change = (data: Model.FromJSONData) => {
        const cid = new Set((g as Graph).getCells().map(c => c.id));
        const t: any[] = []
        if (Array.isArray(data)) {
            t.push(...data)
        } else {
            const { cells = [], nodes = [], edges = [] } = data;
            t.push(...cells, ...nodes, ...edges)
        }
        // 去重
        const ts = new Set(t)
        for (const e of ts) {
            if (cid.has(e.id)) {
                cid.delete(e.id)
                ts.delete(e);
            }
        }
        // 移除多余元素
        g.removeCells(cid, { silent: true })
        // 新增增加的元素
        g.parseJSON(Array.from(ts));
        //
        return g;
    }
    // 重置画布
    g.reset = (d?: Model.FromJSONData) => {
        const data = d || g.toJSON();
        g.destory().recoverConfig().resetOptions().init().refresh(data);
        return g;
    }
    // 缩放
    g.scaleTo = (s: number | "+" | "-" | "=" = g.config.scale / 100) => {
        if (s == '=') {
            g.startAnimate();
            g?.zoomToFit({ padding: 100 });
            return;
        }
        const t = g.config.scale;
        let n = typeof s == 'number' ? s : (s == '+' ? (t + 10) : (t - 10)) / 100
        g?.zoomTo(n)
        return g;
    }
    // // 居中元素
    // g.center = (e:string|Cell) => {
    //     const c = typeof e === 'string' ? g.getCellById(e) : e;
    //     //
    //     g.startAnimate();
    //     g.centerCell(c);
    // }
    // 查询节点 返回匹配的节点列表
    g.search = (v: string | ((cell: Cell) => boolean), names?: string[]) => {
        const cells = g.getCells() as Cell[];
        if (typeof v == 'function') {
            return cells.filter(e => v(e))
        }
        const attrs = names == undefined ? ['label'] : [...names]
        //
        return cells.filter(e => {
            for (const k of attrs) {
                const t = e.prop(k) || get(e, k);
                return new RegExp(v,'i').test(t)
            }
            return false;
        })
    }
    // 布局 需要传入 布局算法函数
    g.layout = (f: (m: any) => any[]) => {
        const m = { nodes: g.getNodes().map((n: any) => ({ id: n.id, position: n.prop('position') })), edges: g.getEdges().map((e: any) => e.toJSON()) }
        const ns = f(m);
        for (const n of ns) {
            g.getCellById(n.id).prop('position', { x: n.x, y: n.y })
        }
        //
        return g;
    }
    // // 获取画布的 JSON 数据
    g.save = () => {
        const d = g.toJSON();
        return {
            time:new Date().toLocaleString(),
            config:g.config,
            ...d,
        }
    }

    g.load = (data:any) => {
        if(data && typeof data == 'object'){
            // check
            if(data.nodes || data.edges || data.cells){
                if(data.config && typeof data.config == 'object'){
                    Object.assign(g.config,data.config);
                    delete data.config;
                }
                g.reset(data);
            }else{
                console.warn(t('graph-load-nodata'))
            }
        }

        return g;
    }

    g.setViewStyle = (cell:Cell,style='') => {
        if(g.findView){
            const view = g.findView(cell);
            if(view){
                view.container.style = style
            }
        }
        return g;
    }

    return g as IPackAntvX6.X6Graph;

}